2 * TETRIS for AVR microcontrollers and cascading LED matrix based on MAX7219 driver
3 * Created for Interplaymediumâ„¢ project (https://interplaymedium.org)
4 * Copyright © 2010 Dmitry Shalnov [interplaymedium.org]
5 * Licensed under the Apache License, Version 2.0
9 #include <avr/interrupt.h>
12 // please change it according to your ARDUINO wiring
13 // don't forget to place pull-up resisor between RESET and +5V pins to avoid board reset during UART transfer.
20 #define INUSE 1 // how many matrix you connect
21 #define STARTX 4 // horisontal position od figure strat
24 // Some macros that make the code more readable
26 #define output_low(port,pin) port &= ~(1<<pin)
27 #define output_high(port,pin) port |= (1<<pin)
28 #define set_input(portdir,pin) portdir &= ~(1<<pin)
29 #define set_output(portdir,pin) portdir |= (1<<pin)
33 #define UBRR FOSC/16/BAUD-1
35 volatile unsigned char u, oldu;
37 // ------------------------------------------- Set Receive Interrupt Enable -------------------------------------------
39 void setRXCIE_USART0()
41 UCSR0B |= _BV(RXCIE0);
44 // -------------------------------------------- Initialize USART0 -----------------------------------------------------
46 void init_USART0 (unsigned int baud) {
47 UBRR0 = baud; // Set Baudrate
48 UCSR0C = (3<<UCSZ00); // Character Size 8 bit
49 UCSR0B |= _BV(RXEN0) | _BV(TXEN0); // Receiver and Transmitter Enable
52 // -------------------------------------------- Receive 1 byte Data --------------------------------------------------
54 unsigned char receive_1byte_USART0 (void) {
55 loop_until_bit_is_set(UCSR0A, RXC0);
59 // -------------------------------------------- Transmit 1 byte Data --------------------------------------------------
61 void transmit_1byte_USART0 (unsigned char data) {
62 loop_until_bit_is_set(UCSR0A, UDRE0);
66 // -------------------------------------------- Interrupt handler -----------------------------------------------------
68 ISR(USART_RX_vect){ // USART RX interrupt
69 volatile unsigned char c;
71 //transmit_1byte_USART0('w');
72 //output_high(PORTB, LED1);
77 // --------------------------------------------- Transmit String Data -------------------------------------------------
79 void transmit_str_USART0 (char *str) {
81 transmit_1byte_USART0(*str);
86 // --------------------------------------------- Transmit Four-Digit Integer ------------------------------------------
88 void transmit_4digit_USART0 (int num) {
94 transmit_1byte_USART0('0'+temp);
100 // --------------------------------------------- Delays --------------------------------------------------------------
102 void delay_ms (uint16_t ms) {
103 uint16_t delay_count = FOSC / 17500 * 0.1;
106 for (i=0; i != delay_count; i++);
111 void delay_ns(uint16_t ms) {
112 uint16_t delay_count = FOSC / 17500 * 0.01;
115 for (i=0; i != delay_count; i++);
120 // --------------------------------------------- Sounds -------------------------------------------------------------
123 for(uint8_t a=0; a<100; a++){
124 output_high(PORTB, BEEPER);
126 output_low(PORTB, BEEPER);
131 void bleepBleepSound (){
133 for(b=1; b<40; b+=10) for(a=0; a<100; a++){
134 output_high(PORTB, BEEPER);
136 output_low(PORTB, BEEPER);
141 // --------------------------------- LED MATRIX code ---------------------------------------------------------------
143 // define max7219 registers
144 uint8_t max7219_reg_noop = 0x00;
145 uint8_t max7219_reg_digit0 = 0x01;
146 uint8_t max7219_reg_digit1 = 0x02;
147 uint8_t max7219_reg_digit2 = 0x03;
148 uint8_t max7219_reg_digit3 = 0x04;
149 uint8_t max7219_reg_digit4 = 0x05;
150 uint8_t max7219_reg_digit5 = 0x06;
151 uint8_t max7219_reg_digit6 = 0x07;
152 uint8_t max7219_reg_digit7 = 0x08;
153 uint8_t max7219_reg_decodeMode = 0x09;
154 uint8_t max7219_reg_intensity = 0x0a;
155 uint8_t max7219_reg_scanLimit = 0x0b;
156 uint8_t max7219_reg_shutdown = 0x0c;
157 uint8_t max7219_reg_displayTest = 0x0f;
160 void putByte(uint8_t data) {
164 mask = 0x01 << (i - 1); // get bitmask
165 //digitalWrite( CLOCK, LOW); // tick
166 output_low(PORTB, CLOCK); // tick
167 if (data & mask){ // choose bit
168 //digitalWrite(DATAIN, HIGH);// send 1
169 output_high(PORTB, DATAIN);
171 //digitalWrite(DATAIN, LOW); // send 0
172 output_low(PORTB, DATAIN);
174 //digitalWrite(CLOCK, HIGH); // tock
175 output_high(PORTB, CLOCK);
176 --i; // move to lesser bit
180 void maxOne(uint8_t maxNr, uint8_t reg, uint8_t col) {
181 //maxOne is for adressing different MAX7219's,
182 //whilele having a couple of them cascaded
184 //digitalWrite(load, LOW); // begin
185 output_low(PORTB, LOAD);
187 for ( c = INUSE; c > maxNr; c--) {
188 putByte(0); // means no operation
189 putByte(0); // means no operation
192 putByte(reg); // specify register
193 putByte(col);//((data & 0x01) * 256) + data >> 1); // put data
195 for ( c = maxNr-1; c >= 1; c--) {
196 putByte(0); // means no operation
197 putByte(0); // means no operation
200 //digitalWrite(load, LOW); // and load da shit
201 output_low(PORTB, LOAD);
202 //digitalWrite(load,HIGH);
203 output_high(PORTB, LOAD);
207 void putPixel (uint8_t maxNr, uint8_t x, uint8_t y){
208 //maxOne is for adressing different MAX7219's,
209 //whilele having a couple of them cascaded
212 //digitalWrite(load, LOW); // begin
213 output_low(PORTB, LOAD);
215 for ( c = INUSE; c > maxNr; c--) {
216 putByte(0); // means no operation
217 putByte(0); // means no operation
220 putByte(y); // specify register
221 putByte(0x01 << (8-x));//((data & 0x01) * 256) + data >> 1); // put data
223 for ( c = maxNr-1; c >= 1; c--) {
224 putByte(0); // means no operation
225 putByte(0); // means no operation
228 //digitalWrite(load, LOW); // and load da shit
229 output_low(PORTB, LOAD);
230 //digitalWrite(load,HIGH);
231 output_high(PORTB, LOAD);
235 // --------------------------------- Figures ----------------------------------------------------------------------
237 uint8_t figure[5][4][4] = {
371 // ----------------------------------------- Init variables ------------------------------------------------------------
373 uint8_t screen[ INUSE*8 + 1 ];
375 uint8_t currentFigure = 4;
376 uint8_t currentTurn = 0;
377 uint8_t currentY = 0;
378 uint8_t currentX = STARTX;
380 uint8_t randomDigit = 0;
381 uint8_t prewRandomDigit;
384 // ------------------------------------------ Game logic ---------------------------------------------------------------
386 uint8_t moveLine(uint8_t L, uint8_t X){
387 if (X <= 4 ) L <<= 4 - X; else L >>= X - 4;
391 void redrawScreen (void) {
392 uint8_t lineToPlace = 0b00000000;
393 for (uint8_t matrix=0; matrix < INUSE; matrix++) for (uint8_t line=0; line<8; line++){
394 if (matrix*8 + line >= currentY && matrix*8 + line < currentY+4) lineToPlace = figure[currentFigure][currentTurn][matrix*8 +line - currentY]; else lineToPlace = 0b00000000;
395 //lineToPlace = 0b11111111; else lineToPlace = 0b00000000;
396 //if (currentX <= 4 ) lineToPlace <<= 4 - currentX; else lineToPlace >>= currentX - 4;
397 lineToPlace = moveLine(lineToPlace, currentX);
398 maxOne(matrix+1, line+1, screen[matrix*8 + line] | lineToPlace );
402 uint8_t checkDown (void){
403 uint8_t lineToPlace = 0b00000000;
404 for (uint8_t line = 0; line<4; line ++){
405 lineToPlace = figure[currentFigure][currentTurn][line];
406 lineToPlace = moveLine(lineToPlace, currentX);
407 if ( (lineToPlace & screen[ currentY+1 + line]) != 0) return 1;
412 uint8_t checkStart (void){
413 uint8_t lineToPlace = 0b00000000;
414 for (uint8_t line = 0; line<4; line ++){
415 lineToPlace = figure[currentFigure][currentTurn][line];
416 lineToPlace = moveLine(lineToPlace, currentX);
417 if ( (lineToPlace & screen[ currentY + line]) != 0) return 1;
422 uint8_t checkLeft (void){
423 uint8_t lineToPlace = 0b00000000;
424 uint8_t tmpLineToPlace;
425 for (uint8_t line = 0; line<4; line ++){
426 lineToPlace = figure[currentFigure][currentTurn][line];
427 tmpLineToPlace = moveLine(lineToPlace, currentX);
428 if ( (tmpLineToPlace & 0b10000000) != 0) return 1;
429 lineToPlace = moveLine(lineToPlace, currentX - 1);
430 if ( (lineToPlace & screen[ currentY + line]) != 0) return 1;
435 uint8_t checkRight (void){
436 uint8_t lineToPlace = 0b00000000;
437 uint8_t tmpLineToPlace;
438 for (uint8_t line = 0; line<4; line ++){
439 lineToPlace = figure[currentFigure][currentTurn][line];
440 tmpLineToPlace = moveLine(lineToPlace, currentX);
441 if ( (tmpLineToPlace & 0b00000001) != 0) return 1;
442 lineToPlace = moveLine(lineToPlace, currentX + 1);
443 if ( (lineToPlace & screen[ currentY + line]) != 0) return 1;
448 uint8_t checkTurn (void){
449 uint8_t lineToPlace = 0b00000000;
451 if (currentTurn < 3) testTurn = currentTurn + 1; else testTurn = 0;
452 for (uint8_t line = 0; line<4; line ++){
453 lineToPlace = figure[currentFigure][testTurn][line];
454 lineToPlace = moveLine(lineToPlace, currentX);
455 //if ( (lineToPlace & 0b00000001) != 0) return 1;
456 if (currentFigure == 1 && currentX == 8) return 1;
457 if (currentFigure == 2 && currentX == 1) return 1;
458 if (currentFigure == 2 && currentX == 8) return 1;
459 if (currentFigure == 3 && currentX == 1) return 1;
460 if (currentFigure == 3 && currentX == 8) return 1;
461 if (currentFigure == 4 && currentX == 2) return 1;
462 if (currentFigure == 4 && currentX == 7) return 1;
463 if ( (lineToPlace & screen[ currentY + line]) != 0) return 1;
468 void checkFullLine (void) {
470 uint8_t matrix = currentY / (INUSE*8);
471 uint8_t remainder = currentY % (INUSE*8);
472 for (uint8_t line = 0; line<4; line ++) if (line + currentY < INUSE*8) {
473 if (screen[line + currentY] == 0b11111111) {
474 for (a = 0; a<3; a++) {
476 maxOne(matrix + 1, line + remainder +1, 0b00000000 );
477 //screen[line + currentY] = 0b11111111;
480 maxOne(matrix + 1, line + remainder +1, 0b11111111 );
481 //screen[line + currentY] = 0b00000000;
485 for (a = line + currentY; a>0; a--) screen[a] = screen[a-1];
487 transmit_str_USART0("Score: ");
488 transmit_4digit_USART0(score++);
489 transmit_str_USART0(" \r");
495 uint8_t ornament[3] = {0b00100100, 0b10010010, 0b01001001};
497 for (uint8_t matrix=0; matrix < INUSE; matrix++) for (uint8_t line=0; line<8; line++){
498 maxOne(matrix + 1, line + 1, 0b11111111 );
501 for (uint8_t matrix=0; matrix < INUSE; matrix++) for (uint8_t line=0; line<8; line++){
502 maxOne(matrix + 1, line + 1, 0b00000000 );
503 screen[ matrix*8 + line] = 0b00000000;
508 for (uint8_t matrix=0; matrix < INUSE; matrix++) for (uint8_t line=0; line<8; line++){
509 maxOne(matrix + 1, line + 1, ornament[(line+offset) % 3] );
511 if (offset < 2) offset ++; else offset = 0;
516 transmit_str_USART0("Score: 0000 \r");
518 currentFigure = (uint8_t)((uint16_t)timer % (uint16_t)5);
523 void stopFigure (void) {
524 uint8_t lineToPlace = 0b00000000;
525 for (uint8_t line = 0; line<4; line ++) {
526 lineToPlace = figure[currentFigure][currentTurn][line];
527 lineToPlace = moveLine(lineToPlace, currentX);
528 screen[line + currentY] |= lineToPlace;
534 currentFigure = randomDigit;
536 if (checkStart () != 1) redrawScreen (); else gameOver();
539 // --------------------------------- MAIN -----------------------------------------------------------------------
543 // initialize the direction of PORTD to be output
545 set_output(DDRB, DATAIN);
546 set_output(DDRB, LOAD);
547 set_output(DDRB, CLOCK);
548 set_output(DDRB, BEEPER);
550 // initiation of the max 7219
551 for (uint8_t matrix=0; matrix < INUSE; matrix++) {
552 maxOne(matrix+1, max7219_reg_scanLimit, 0x07);
553 maxOne(matrix+1, max7219_reg_decodeMode, 0x00); // using a led matrix, not digits
554 maxOne(matrix+1, max7219_reg_shutdown, 0x01); // not in shutdown mode
555 maxOne(matrix+1, max7219_reg_displayTest, 0x00); // no display test
556 for (uint8_t line=0; line<8; line ++) maxOne(matrix+1, line+1, 0); // empty registers, turn all LEDs off
557 maxOne(matrix+1, max7219_reg_intensity, 0x0f & 0x0f); // the first 0x0f is the value you can set (range: 0x00 to 0x0f)
560 init_USART0(UBRR); // initialize USART0
564 screen[ INUSE*8 ] = 0b11111111; // bottom border for checking
565 currentFigure = (uint8_t)((uint16_t)timer % (uint16_t)RAND_MAX);
566 transmit_str_USART0("Score: 0000 \r");
569 if (timer % 20000 == 0) {
571 if (checkDown() != 1) currentY ++; else {
572 if (prewRandomDigit == randomDigit) randomDigit++;
573 if (randomDigit > RAND_MAX+1) randomDigit = 0;
574 prewRandomDigit = randomDigit;
581 randomDigit = (uint8_t)((uint16_t)timer % (uint16_t)RAND_MAX);
582 //transmit_1byte_USART0(timer);
583 //transmit_4digit_USART0(u);
585 //transmit_str_USART0(" RIGHT \r");
586 if (checkRight () != 1) currentX ++;
591 //transmit_str_USART0(" LEFT \r");
592 if (checkLeft () != 1) currentX --;
597 //transmit_str_USART0(" DOWN \r");
598 if (checkDown() != 1) currentY ++;
603 //transmit_str_USART0(" UP \r");
604 if (checkTurn () != 1) {
605 if (currentTurn < 3) currentTurn ++; else currentTurn = 0;